# Module 'panel'
#
# Prise en charge de la bibliothèque Panel.
# Utilise le module intégré 'pnl'.
# Il est conseillé d'utiliser 'panel.fonction' et non 'pnl.fonction' dans
# vos applications; la plupart des fonctions du module 'pnl' sont exportées
# de manière transparente par 'panel'; néanmoins, pour la fonction dopanel()
# qui est surchargée, vous devez utiliser la version de ce module si vous
# souhaitez utiliser le mécanisme des callbacks.


import pnl


debug = 0


# Teste si un objet est une liste.
#
def is_list(x):
	return type(x) = type([])


# Inverse l'ordre des éléments d'une liste.
#
def reverse(list):
	res = []
	for item in list:
		res.insert(0, item)
	return res


# Obtient un attribut d'une liste, ce dernier pouvant être une autre
# liste. Le nom de l'attribut ne doit pas être 'prop'.
#
def getattrlist(list, name):
	for item in list:
		if item and is_list(item) and item[0] = name:
			return item[1:]
	return []


# Obtient une propriété d'une liste, cette dernière pouvant être
# elle-même une autre liste.
#
def getproplist(list, name):
	for item in list:
		if item and is_list(item) and item[0] = 'prop':
			if len(item) > 1 and item[1] = name:
				return item[2:]
	return []


# Teste si la description contient la propriété 'end-of-group'
#
def is_endgroup(list):
	x = getproplist(list, 'end-of-group')
	return (x and x[0] = '#t')


# Affiche proprement la définition d'un actuateur spécifié
# sous forme d'une S-expression; la chaîne prefix s'affiche
# avant chaque ligne.
#
def show_actuator(prefix, a):
	for item in a:
		if not is_list(item):
			print prefix, item
		elif item and item[0] = 'al':
			print prefix, 'Liste des sous-actuateurs :'
			for a in item[1:]:
				show_actuator(prefix + '    ', a)
		elif len(item) = 2:
			print prefix, item[0], '=>', item[1]
		elif len(item) = 3 and item[0] = 'prop':
			print prefix, 'Prop', item[1], '=>',
			print item[2]
		else:
			print prefix, '?', item


# Affiche proprement un panel.
#
def show_panel(prefix, p):
	for item in p:
		if not is_list(item):
			print prefix, item
		elif item and item[0] = 'al':
			print prefix, 'Liste des actuateurs :'
			for a in item[1:]:
				show_actuator(prefix + '    ', a)
		elif len(item) = 2:
			print prefix, item[0], '=>', item[1]
		elif len(item) = 3 and item[0] = 'prop':
			print prefix, 'Prop', item[1], '=>',
			print item[2]
		else:
			print prefix, '?', item


# Exception déclenchée par build_actuator ou build_panel.
#
panel_error = 'erreur du module panel'


# Callback stupide servant à initialiser les autres callbacks.
#
def dummy_callback(arg):
	pass


# Affecte des attributs à la cible; ces attributs deviennent des membres de
# la cible.
# Les attributs dont les noms sont contenus dans exclist sont ignorés.
# Le nom du membre est le nom de l'attribut avec prefix ajouté au début.
#
def assign_members(target, attrlist, exclist, prefix):
	for item in attrlist:
		if is_list(item) and len(item) = 2 and item[0] not in exclist:
			name, value = item[0], item[1]
			ok = 1
			if value[0] in '-0123456789':
				value = eval(value)
			elif value[0] = '"':
				value = value[1:-1]
			elif value = 'move-then-resize':
				# Une étrange valeur par défaut définie par le programme
				# Panel Editor...
				ok = 0
			else:
				print 'la valeur', value, 'de', name, 'est inconnue'
				ok = 0
			if ok:
				lhs = 'target.' + prefix + name
				stmt = lhs + '=' + `value`
				if debug: print 'exec', stmt
				try:
					exec(stmt + '\n')
				except KeyboardInterrupt: # N'interceptez pas ça!
					raise KeyboardInterrupt
				except:
					print 'l\'affectation a échoué :', stmt


# Construit un actuateur à partir de la description d'un actuateur.
# Renvoie une paire (actuateur, nom).
#
def build_actuator(descr):
	namelist = getattrlist(descr, 'name')
	if namelist:
		# On suppose que c'est une chaîne
		actuatorname = namelist[0][1:-1]
	else:
		actuatorname = ''
	type = descr[0]
	if type[:4] = 'pnl_': type = type[4:]
	act = pnl.mkact(type)
	act.downfunc = act.activefunc = act.upfunc = dummy_callback
	#
	assign_members(act, descr[1:], ('al', 'data', 'name'), '')
	#
	# On traite les données spécifiques à l'actuateur
	#
	datalist = getattrlist(descr, 'data')
	prefix = ''
	if type[-4:] = 'puck':
		prefix = 'puck_'
	elif type = 'mouse':
		prefix = 'mouse_'
	assign_members(act, datalist, (), prefix)
	#
	return act, actuatorname


# Construit tous les sous-actuateurs et les ajoute au super-actuateur.
# Le super-actuateur doit déjà avoir été ajouté au panel. Les
# sous-actuateurs dont les noms sont définis sont ajoutés au panel
# comme des membres du panel, pour qu'on puisse les référencer au
# moyen de la notation p.nom.
#
# Note : je ne sais pas comment panel.endgroup() fonctionne lorsqu'on
# l'applique à un sous-actuateur.
#
def build_subactuators(panel, super_act, al):
	#
	# Cette boucle est presque la même que celle de build_panel(),
	# seulement elle appelle addsubact() et pas addact().
	#
	for a in al:
		act, name = build_actuator(a)
		act.addsubact(super_act)
		if name:
			stmt = 'panel.' + name + ' = act'
			if debug: print 'exec', stmt
			exec(stmt + '\n')
		if is_endgroup(a):
			panel.endgroup()
		sub_al = getattrlist(a, 'al')
		if sub_al:
			build_subactuators(panel, act, sub_al)
	#
	# On corrige l'actuateur à qui on vient d'ajouter des sous-actuateurs.
	# Cela ne fait pas de mal (j'espère) et c'est nécessaire pour
	# l'actuateur de défilement.
	#
	super_act.fixact()


# Construit un panel à partir de la définition d'un panel.
# Renvoie un objet panel p, où pour chaque actuateur a portant un nom, p.nom 
# est une référence à a.
#
def build_panel(descr):
	#
	# Contrôle sanitaire
	#
	if (not descr) or descr[0] <> 'panel':
		raise panel_error, 'la description d\'un panel doit commencer par "panel"'
	#
	if debug: show_panel('', descr)
	#
	# On crée un panel vide
	#
	panel = pnl.mkpanel()
	#
	# On affecte des attributs au panel
	#
	assign_members(panel, descr[1:], ('al'), '')
	#
	# On va chercher la liste des actuateurs du panel
	#
	al = getattrlist(descr, 'al')
	#
	# À cause de l'opérateur endgroup(), l'ordre de création des
	# actuateurs est important.
	# Malheureusement, le programme Panel Editor génère une liste
	# d'actuateurs qui est dans l'ordre inverse de celui qu'on
	# désire; on doit donc inverser cet ordre ici.
	#
	al = reverse(al)
	#
	for a in al:
		act, name = build_actuator(a)
		act.addact(panel)
		if name:
			stmt = 'panel.' + name + ' = act'
			exec(stmt + '\n')
		if is_endgroup(a):
			panel.endgroup()
		sub_al = getattrlist(a, 'al')
		if sub_al:
			build_subactuators(panel, act, sub_al)
	#
	return panel


# Cette fonction est construite autour de pnl.dopanel() et implémente
# les appels des callbacks.
#
def my_dopanel():
	# On extrait seulement les 4 premiers éléments pour permettre une
	# expansion future
	a, down, active, up = pnl.dopanel()[:4]
	if down:
		down.downfunc(down)
	if active:
		active.activefunc(active)
	if up:
		up.upfunc(up)
	return a


# Crée un panel ou plus à partir d'un fichier de description (contenant
# des S-expressions) généré par le programme Panel Editor.
# 
def defpanellist(file):
	import parser
	descrlist = parser.parse_file(open(file, 'r'))
	panellist = []
	for descr in descrlist:
		panellist.append(build_panel(descr))
	return panellist


# Importe tous les éléments du module intégré pnl à l'intérieur de ce
# module pour que l'utilisateur puisse toujours utiliser panel.toto()
# au lieu de pnl.toto().
# Une fois que ce module est importé, *il n'y a pas* de différence de 
# performance entre le module intégré et ce module.
#
from pnl import *			# for export

dopanel = my_dopanel			# override pnl.dopanel
